using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using RPGOnline.Extensions;
using RPGOnline.Rendering;
using Matrix = Microsoft.DirectX.Matrix;

namespace RPGOnline.Controls.Map.Layers.Lighting
{
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class Light
    {
        private Map m_map;
        private Vector2 m_position;
        private float m_radius;
        private Color m_color;
        private bool m_invalid = true;

	    private Matrix m_renderMatrix;
		private Matrix m_textureMatrix;
	    private Texture m_occlusionMap;
	    private Texture m_lightMap;
	    private bool m_radiusChanged;

	    [Browsable(false)]
        public Map Map
        {
            get { return m_map; }
            set { m_map = value; }
        }

        protected Light()
        {
            Position = new Vector2(0, 0);
            Radius = 100;
            Color = Color.White;
        }

        public Light(Map map)
            : this()
        {
            Map = map;
        }

        public Vector2 Position
        {
            get { return m_position; }
            set
            {
                m_position = value;
                m_invalid = true;
            }
        }
        public float Radius
        {
            get { return m_radius; }
            set
            {
                m_radius = value;
                m_invalid = true;
	            m_radiusChanged = true;
            }
        }
        public Color Color
        {
            get { return m_color; }
            set
            {
                m_color = value;
            }
        }

	    public RectangleF GetBounds()
	    {
			RectangleF bounds = new Rectangle(new Point((int)m_position.X, (int)m_position.Y), Size.Empty);
			bounds.Inflate(Radius, Radius);
		    return bounds;
	    }

        public bool DropShadows { get; set; }

		public void UpdateOcclusionMap(IRenderer renderer)
	    {
			if (!m_invalid)
				return;

			UpdateVertexBuffer(renderer);
			var bounds = GetBounds();

			using (renderer.BeginOcclusionMap(m_occlusionMap, bounds, m_radius))
			{
				renderer.Clear(Color.White);
				
				m_map.DrawObstructionMap();
			}

			using (renderer.BeginShadowCast(m_lightMap, m_radius))
			{
				renderer.Clear(Color.FromArgb(0, Color.Black));

				renderer.DrawQuad(Matrix.Scaling(256, 1, 1), Matrix.Identity, m_occlusionMap);
			}
	    }

		public void Draw(IRenderer renderer)
        {
			UpdateVertexBuffer(renderer);
			
			using (renderer.BeginLight(m_lightMap, m_color))
			{
				renderer.DrawQuad(m_renderMatrix, m_textureMatrix, renderer.GetFirstPassTexture());
			}
        }

		private void UpdateVertexBuffer(IRenderer renderer)
        {
            if (!m_invalid) 
				return;

	        if (m_occlusionMap == null || m_radiusChanged)
	        {
		        if (m_occlusionMap != null)
			        m_occlusionMap.Dispose();

		        m_occlusionMap = renderer.CreateRenderTarget((int) Math.Ceiling(m_radius*2), (int) Math.Ceiling(m_radius*2));
	        }

	        if(m_lightMap == null)
				m_lightMap = renderer.CreateRenderTarget(256, 1);

	        m_renderMatrix = Matrix.Scaling(m_radius*2, m_radius*2, 1) * Matrix.Translation(m_position.X - m_radius, m_position.Y - m_radius, 0);

			var onePixelInWidth = 1.0f / (m_map.MapSize.Width * m_map.TileSize.Width);
			var onePixelInHeight = 1.0f / (m_map.MapSize.Height * m_map.TileSize.Height);

			m_textureMatrix =
				Matrix.Scaling(m_radius * 2 * onePixelInWidth, m_radius * 2 * onePixelInHeight, 1) *
				TextureMatrix.Translate2D((m_position.X - m_radius) * onePixelInWidth, (m_position.Y - m_radius) * onePixelInHeight);
			

			m_invalid = false;
	        m_radiusChanged = false;
        }

		public void DrawPlaceholder(IRenderer renderer)
		{
			renderer.DrawLightPlaceholder(m_position, m_radius, m_color);
		}
    }
}